﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;

namespace MagicCube
{
    public enum Spindle { X, Y, Z }
    class CubeModel
    {
        #region 方块属性

        //立方体的面，即正方形
        private MeshGeometry3D[] faces = new MeshGeometry3D[6];
        //材质，6为内层
        private DiffuseMaterial[] material = new DiffuseMaterial[7]; 
        //构成面的点坐标
        private Point3D[] points = new Point3D[8];
        //面坐标分布
        private Int32Collection pointIndices = new Int32Collection();

        #endregion

        #region 动画属性

        //转轴类
        private AxisAngleRotation3D axis = new AxisAngleRotation3D(new Vector3D(0, 0, 0), 0);
        //默认转心
        private Point3D axisC = new Point3D(0, 0, 0);
        //默认转轴X
        private Vector3D axisX = new Vector3D(1, 0, 0);
        //默认转轴Y
        private Vector3D axisY = new Vector3D(0, 1, 0);
        //默认转轴Z
        private Vector3D axisZ = new Vector3D(0, 0, 1);
        //默认旋转角度
        private const double angle = 90;
        //默认动画周期
        private Duration period = new Duration(TimeSpan.FromMilliseconds(200));

        #endregion

        #region 外部接口

        /// <summary>
        /// 获取立方体组
        /// </summary>
        private Model3DGroup cubeGroup = new Model3DGroup();
        /// <summary>
        /// 获取立方体组
        /// </summary>
        public Model3DGroup CubeGroup { get { return cubeGroup; } }

        /// <summary>
        /// 中心坐标
        /// </summary>
        private Point3D pointC = new Point3D();
        /// <summary>
        /// 获取中心坐标
        /// </summary>
        public Point3D PointC { get { return pointC; } }

        /// <summary>
        /// 边长
        /// </summary>
        private double length = 2;
        /// <summary>
        /// 获取边长
        /// </summary>
        public double Length { get { return length; } }

        /// <summary>
        /// 构造器
        /// </summary>
        public CubeModel()
            :this(0, 0, 0)
        {
        }

        /// <summary>
        /// 带参数构造器
        /// </summary>
        /// <param name="deviantX">X坐标</param>
        /// <param name="deviantY">Y坐标</param>
        /// <param name="deviantZ">Z坐标</param>
        public CubeModel(double deviantX, double deviantY, double deviantZ)
        {
            Initialize_Points(deviantX, deviantY, deviantZ);
            Initialize_CenterPoint();
            Initialize_Faces();
            Initialize_FacePoint();
            Initialize_TriangleIndices();
            Initialize_Material();
            Create_3DCube();
        }

        #endregion

        #region 初始化函数

        //初始化点坐标
        private void Initialize_Points()
        {
            points[0] = new Point3D(-0.5 * length, 0.5 * length, 0.5 * length);
            points[1] = new Point3D(-0.5 * length, -0.5 * length, 0.5 * length);
            points[2] = new Point3D(0.5 * length, -0.5 * length, 0.5 * length);
            points[3] = new Point3D(0.5 * length, 0.5 * length, 0.5 * length);
            points[4] = new Point3D(0.5 * length, 0.5 * length, -0.5 * length);
            points[5] = new Point3D(0.5 * length, -0.5 * length, -0.5 * length);
            points[6] = new Point3D(-0.5 * length, -0.5 * length, -0.5 * length);
            points[7] = new Point3D(-0.5 * length, 0.5 * length, -0.5 * length);
        }
        //根据偏移值初始化点坐标
        private void Initialize_Points(double deviantX, double deviantY, double deviantZ)
        {
            Initialize_Points();
            if (deviantX == 0 && deviantY == 0 && deviantZ == 0) return;
            //长度
            deviantX *= length;
            deviantY *= length; 
            deviantZ *= length;
            //偏移
            for (int i = 0; i < 8; i++)
            { 
                points[i].X += deviantX;
                points[i].Y += deviantY;
                points[i].Z += deviantZ;
            }
        }
        //初始化中心坐标
        private void Initialize_CenterPoint()
        {
            pointC.X = (points[0].X + points[5].X) / 2;
            pointC.Y = (points[0].Y + points[5].Y) / 2;
            pointC.Z = (points[0].Z + points[5].Z) / 2;
        }
        //初始化面
        private void Initialize_Faces()
        {
            for (int i = 0; i < 6; i++)
            {
                faces[i] = new MeshGeometry3D();
            }
        }
        //初始化面坐标点
        private void Initialize_FacePoint()
        {
            //清空面坐标
            for (int i = 0; i < 6; i++)
            {
                faces[i].Positions.Clear();
            }

            faces[0].Positions.Add(points[0]); 
            faces[0].Positions.Add(points[1]); 
            faces[0].Positions.Add(points[2]);
            faces[0].Positions.Add(points[3]);

            faces[1].Positions.Add(points[4]); 
            faces[1].Positions.Add(points[5]); 
            faces[1].Positions.Add(points[6]);
            faces[1].Positions.Add(points[7]);

            faces[2].Positions.Add(points[0]);
            faces[2].Positions.Add(points[3]);
            faces[2].Positions.Add(points[4]);
            faces[2].Positions.Add(points[7]);

            faces[3].Positions.Add(points[2]); 
            faces[3].Positions.Add(points[1]);
            faces[3].Positions.Add(points[6]);
            faces[3].Positions.Add(points[5]);

            faces[4].Positions.Add(points[0]);
            faces[4].Positions.Add(points[7]);
            faces[4].Positions.Add(points[6]);
            faces[4].Positions.Add(points[1]);

            faces[5].Positions.Add(points[4]); 
            faces[5].Positions.Add(points[3]); 
            faces[5].Positions.Add(points[2]);
            faces[5].Positions.Add(points[5]);
        }
        //初始化点列表，用于构靠正方形
        private void Initialize_TriangleIndices()
        {
            pointIndices.Add(0);
            pointIndices.Add(1);
            pointIndices.Add(2);
            pointIndices.Add(2);
            pointIndices.Add(3);
            pointIndices.Add(0);
        }
        //初始化材质
        private void Initialize_Material()
        {
            material[0] = new DiffuseMaterial(new SolidColorBrush(Colors.Red));
            material[1] = new DiffuseMaterial(new SolidColorBrush(Colors.Orange));
            material[2] = new DiffuseMaterial(new SolidColorBrush(Colors.Blue));
            material[3] = new DiffuseMaterial(new SolidColorBrush(Colors.Green));
            material[4] = new DiffuseMaterial(new SolidColorBrush(Colors.White));
            material[5] = new DiffuseMaterial(new SolidColorBrush(Colors.Yellow));
            material[6] = new DiffuseMaterial(new SolidColorBrush(Colors.Gray));
        }
        // 生成模型
        private void Create_3DCube()
        {
            GeometryModel3D geometry = new GeometryModel3D();
            for (int i = 0; i < 6; i++)
            {
                faces[i].TriangleIndices = pointIndices;
                geometry = new GeometryModel3D(faces[i], material[i]);
                geometry.BackMaterial = material[6];
                cubeGroup.Children.Add(geometry);
            }
            //为模型组添加旋转属性
            RotateTransform3D rotate = new RotateTransform3D(axis, axisC);
            cubeGroup.Transform = rotate;
        }

        #endregion

        #region 处理函数

        //旋转
        public DoubleAnimation Rotate_CubeModel(Spindle spindle, bool isClockwise, FrameworkElement element, string axisName)
        {
            double angleR = 0;
            if (isClockwise) angleR = -angle;
            else angleR = angle;
            switch (spindle)
            {
                case Spindle.X: axis.Axis = axisX; break;
                case Spindle.Y: axis.Axis = axisY; break;
                case Spindle.Z: axis.Axis = axisZ; break;
            }
            DoubleAnimation da = new DoubleAnimation(angleR, period, FillBehavior.Stop);
            element.RegisterName(axisName, axis);
            Storyboard.SetTargetName(da, axisName);
            Storyboard.SetTargetProperty(da, new PropertyPath("(AxisAngleRotation3D.Angle)"));
            return da;
        }
        //更新方块位置
        public void UpdateLocation(CubeModel other, Spindle spindle, bool isClockwise)
        {
            switch (spindle)
            {
                case Spindle.X: RotateByX(other, isClockwise); break;
                case Spindle.Y: RotateByY(other, isClockwise); break;
                case Spindle.Z: RotateByZ(other, isClockwise); break;
            }
            Initialize_FacePoint();
        }
        //沿X轴旋转替换
        private void RotateByX(CubeModel other, bool isClockwise)
        {
            if (isClockwise)
            {
                for (int i = 0; i < 8; i++)
                {
                    points[i] = new Point3D(points[i].X, points[i].Z, -points[i].Y);
                }
                pointC = new Point3D(pointC.X, pointC.Z, -pointC.Y);
            }
            else
            {
                for (int i = 0; i < 8; i++)
                {
                    points[i] = new Point3D(points[i].X, -points[i].Z, points[i].Y);
                }
                pointC = new Point3D(pointC.X, -pointC.Z, pointC.Y);
            }
        }
        //沿Y轴旋转替换
        private void RotateByY(CubeModel other, bool isClockwise)
        {
            if (isClockwise)
            {
                for (int i = 0; i < 8; i++)
                {
                    points[i] = new Point3D(-points[i].Z, points[i].Y, points[i].X);
                }
                pointC = new Point3D(-pointC.Z, pointC.Y, pointC.X);
            }
            else
            {
                for (int i = 0; i < 8; i++)
                {
                    points[i] = new Point3D(points[i].Z, points[i].Y, -points[i].X);
                }
                pointC = new Point3D(pointC.Z, pointC.Y, -pointC.X);
            }
        }
        //沿Y轴旋转替换
        private void RotateByZ(CubeModel other, bool isClockwise)
        {
            if (isClockwise)
            {
                for (int i = 0; i < 8; i++)
                {
                    points[i] = new Point3D(points[i].Y, -points[i].X, points[i].Z);
                }
                pointC = new Point3D(pointC.Y, -pointC.X, pointC.Z);
            }
            else
            {
                for (int i = 0; i < 8; i++)
                {
                    points[i] = new Point3D(-points[i].Y, points[i].X, points[i].Z);
                }
                pointC = new Point3D(-pointC.Y, pointC.X, pointC.Z);
            }
        }

        #endregion
    }
}
